package cn.index.dev.tech.trigger.http;

import cn.index.dev.tech.api.IRAGService;
import cn.index.dev.tech.api.response.Response;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.redisson.api.RList;
import org.redisson.api.RedissonClient;
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.tika.TikaDocumentReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.PgVectorStore;
import org.springframework.core.io.FileSystemResource;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@Slf4j
@RestController()
@CrossOrigin("*")
@RequestMapping("/api/v1/rag/")
public class RAGController implements IRAGService {

    @Resource
    private TokenTextSplitter tokenTextSplitter;
    @Resource
    private PgVectorStore pgVectorStore;
    @Resource
    private RedissonClient redissonClient;

    @RequestMapping(value = "query_rag_tag_list", method = RequestMethod.GET)
    @Override
    public Response<List<String>> queryArgTagList() {
        RList<String> elements = redissonClient.getList("ragTag");
        return Response.<List<String>>builder()
                .code("0000")
                .info("调用成功")
                .data(elements)
                .build();
    }

    @RequestMapping(value = "file/upload", method = RequestMethod.POST, headers = "content-type=multipart/form-data")
    @Override
    public Response<String> uploadFile(@RequestParam("ragTag") String ragTag, @RequestParam("file") List<MultipartFile> files) {
        long startTime = System.currentTimeMillis();
        log.info("上传知识库开始 {}, 文件数量: {}", ragTag, files.size());

        // 批量处理所有文件的文档
        List<Document> allSplitDocuments = new ArrayList<>();

        // 并行处理文件
        ExecutorService executor = Executors.newFixedThreadPool(Math.min(Runtime.getRuntime().availableProcessors(), files.size()));
        List<Future<List<Document>>> futures = new ArrayList<>();

        try {
            // 1. 并行加载知识库为document
            for (MultipartFile file : files) {
                futures.add(executor.submit(() -> {
                    try {
                        // 对于文本文件使用更高效的读取方式
                        String fileName = file.getOriginalFilename();
                        List<Document> documents;

                        if (fileName != null && isTextFile(fileName)) {
                            // 直接读取文本文件内容
                            String content = new String(file.getBytes(), StandardCharsets.UTF_8);
                            Document doc = new Document(content, Collections.singletonMap("filename", fileName));
                            documents = Collections.singletonList(doc);
                        } else {
                            // 其他文件类型使用Tika
                            TikaDocumentReader reader = new TikaDocumentReader(file.getResource());
                            documents = reader.get();
                        }

                        // 分割文档
                        List<Document> splitDocs = tokenTextSplitter.apply(documents);
                        // 打标签
                        splitDocs.forEach(doc -> doc.getMetadata().put("knowledge", ragTag));
                        return splitDocs;
                    } catch (Exception e) {
                        log.error("处理文件失败: {}", file.getOriginalFilename(), e);
                        return Collections.emptyList();
                    }
                }));
            }

            // 收集所有处理结果
            for (Future<List<Document>> future : futures) {
                try {
                    allSplitDocuments.addAll(future.get());
                } catch (Exception e) {
                    log.error("获取文件处理结果失败", e);
                }
            }

            // 2. 批量入向量库，分批次处理以提高性能
            final int batchSize = 100; // 每批处理的文档数量
            for (int i = 0; i < allSplitDocuments.size(); i += batchSize) {
                int end = Math.min(i + batchSize, allSplitDocuments.size());
                List<Document> batch = allSplitDocuments.subList(i, end);
                pgVectorStore.accept(batch);
                log.debug("批量存储文档片段: {}-{}/{} 完成", i, end - 1, allSplitDocuments.size());
            }

            // 3. 存储知识库记录到redis
            RList<Object> ragList = redissonClient.getList("ragTag");
            if (!ragList.contains(ragTag)) {
                ragList.add(ragTag);
            }

            long endTime = System.currentTimeMillis();
            log.info("上传知识库完成 {}, 共处理 {} 个文档片段，耗时 {} 毫秒",
                    ragTag, allSplitDocuments.size(), (endTime - startTime));

            return Response.<String>builder()
                    .code("0000")
                    .info("调用成功")
                    .data("共处理 " + allSplitDocuments.size() + " 个文档片段")
                    .build();

        } catch (Exception e) {
            log.error("上传知识库失败", e);
            return Response.<String>builder()
                    .code("9999")
                    .info("上传失败: " + e.getMessage())
                    .build();
        } finally {
            executor.shutdown();
        }
    }

    /**
     * 判断是否为文本文件
     */
    private boolean isTextFile(String fileName) {
        String[] textExtensions = {".txt", ".md", ".java", ".py", ".js", ".ts", ".html", ".css", ".xml", ".json", ".properties", ".yml", ".yaml"};
        String lowerCaseName = fileName.toLowerCase();
        for (String ext : textExtensions) {
            if (lowerCaseName.endsWith(ext)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 新增标签
     * 与上传知识库同一类中，编码风格保持一致，使用 Redisson 存储。
     */
    @RequestMapping(value = "tag/add", method = RequestMethod.POST)
    public Response<String> addTag(@RequestParam("ragTag") String ragTag) {
        log.info("新增标签开始 {}", ragTag);
        RList<String> ragList = redissonClient.getList("ragTag");
        if (!ragList.contains(ragTag)) {
            ragList.add(ragTag);
            log.info("新增标签完成 {}", ragTag);
            return Response.<String>builder().code("0000").info("调用成功").build();
        } else {
            log.info("标签已存在 {}", ragTag);
            return Response.<String>builder().code("0000").info("标签已存在").build();
        }
    }

    @RequestMapping(value = "git/analyzeGitRepository", method = RequestMethod.POST)
    @Override
    public Response<String> analyzeGitRepository(@RequestParam String gitRepoUrl, @RequestParam String token) {
        long startTime = System.currentTimeMillis();
        log.info("开始分析Git仓库: {}", gitRepoUrl);
        String ragTag = "repository";
        // 确保temp目录存在
        File tempDir = new File("./temp");
        if (!tempDir.exists()) {
            tempDir.mkdirs();
        }

        String localPath = tempDir.getAbsolutePath() + "/git-repo-" + System.currentTimeMillis();

        try {
            // 1. 从Git拉取代码
            Git git = Git.cloneRepository()
                    .setURI(gitRepoUrl)
                    .setCredentialsProvider(new UsernamePasswordCredentialsProvider("", token))
                    .setDirectory(new File(localPath))
                    .setBranch("master") // 默认拉取master分支
                    .call();
            git.close();
            log.info("Git仓库拉取成功，保存在: {}", localPath);

            // 2. 解析代码文件（并行处理）
            File repoDir = new File(localPath);
            List<File> codeFiles = new ArrayList<>();
            collectCodeFiles(repoDir, codeFiles);

            log.info("发现 {} 个代码文件，开始并行处理", codeFiles.size());

            // 使用线程池并行处理文件
            ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
            List<Future<List<Document>>> futures = new ArrayList<>();

            for (File file : codeFiles) {
                futures.add(executor.submit(() -> {
                    try {
                        return processCodeFile(file);
                    } catch (Exception e) {
                        log.warn("处理文件失败: {}", file.getPath(), e);
                        return Collections.emptyList();
                    }
                }));
            }

            // 收集所有处理结果
            List<Document> allDocuments = new ArrayList<>();
            for (Future<List<Document>> future : futures) {
                try {
                    allDocuments.addAll(future.get());
                } catch (Exception e) {
                    log.error("获取文件处理结果失败", e);
                }
            }

            executor.shutdown();
            log.info("文件处理完成，共 {} 个文档", allDocuments.size());

            // 3. 加载到向量库（深度优化）
            if (!allDocuments.isEmpty()) {
                // 先添加标签再进行分割
                allDocuments.forEach(doc -> doc.getMetadata().put("knowledge", ragTag));
                List<Document> documentSplitterList = tokenTextSplitter.apply(allDocuments);
                log.info("文档分割完成，共 {} 个片段", documentSplitterList.size());

                // 关键优化：增大批处理大小，减少数据库连接次数
                final int batchSize = 50; // 增大批次以减少数据库交互次数
                long storeStartTime = System.currentTimeMillis();
                
                // 减少日志和时间计算开销
                for (int i = 0; i < documentSplitterList.size(); i += batchSize) {
                    int end = Math.min(i + batchSize, documentSplitterList.size());
                    // 直接使用子列表视图，减少内存分配
                    List<Document> batch = documentSplitterList.subList(i, end);
                    
                    // 直接存储，避免不必要的时间计算
                    pgVectorStore.accept(batch);
                    
                    // 只在必要时记录日志
                    if (i % 100 == 0) {
                        log.debug("已处理 {} / {} 个文档片段", i, documentSplitterList.size());
                    }
                }

                long storeEndTime = System.currentTimeMillis();
                log.info("成功加载 {} 个文档片段到向量库，耗时 {} 毫秒",
                        documentSplitterList.size(), (storeEndTime - storeStartTime));
            }

            // 4. 将ragTag存储到redis
            RList<Object> ragList = redissonClient.getList("ragTag");
            if (!ragList.contains(ragTag)) {
                ragList.add(ragTag);
            }

            // 清理临时文件
            deleteDirectory(repoDir);

            long endTime = System.currentTimeMillis();
            log.info("Git仓库分析完成，总耗时 {} 毫秒", (endTime - startTime));

            return Response.<String>builder()
                    .code("0000")
                    .info("Git仓库分析成功")
                    .data("成功加载 " + (allDocuments.isEmpty() ? 0 : tokenTextSplitter.apply(allDocuments).size()) + " 个文档片段到知识库")
                    .build();

        } catch (Exception e) {
            log.error("分析Git仓库失败", e);
            // 清理临时文件
            try {
                deleteDirectory(new File(localPath));
            } catch (Exception ignored) {
            }

            return Response.<String>builder()
                    .code("9999")
                    .info("Git仓库分析失败: " + e.getMessage())
                    .build();
        }
    }

    /**
     * 收集目录下的所有代码文件
     */
    private void collectCodeFiles(File directory, List<File> codeFiles) {
        if (directory == null || !directory.exists() || !directory.isDirectory()) {
            return;
        }

        // 忽略特定目录
        String[] ignoreDirs = {".git", "target", "build", "node_modules", ".idea", ".vscode"};
        for (String ignoreDir : ignoreDirs) {
            if (directory.getName().equals(ignoreDir)) {
                return;
            }
        }

        // 支持的代码文件扩展名
        String[] supportedExtensions = {".java", ".py", ".js", ".ts", ".html", ".css", ".xml", ".json", ".properties", ".yml", ".yaml", ".md", ".txt"};

        File[] files = directory.listFiles();
        if (files == null) {
            return;
        }

        for (File file : files) {
            if (file.isDirectory()) {
                // 递归处理子目录
                collectCodeFiles(file, codeFiles);
            } else {
                // 检查文件扩展名
                String fileName = file.getName();
                for (String ext : supportedExtensions) {
                    if (fileName.endsWith(ext)) {
                        codeFiles.add(file);
                        break;
                    }
                }
            }
        }
    }

    /**
     * 处理单个代码文件，返回文档列表
     */
    private List<Document> processCodeFile(File file) throws IOException {
        // 直接读取文本文件内容，避免使用Tika的开销
        StringBuilder contentBuilder = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(
                new FileInputStream(file), StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                contentBuilder.append(line).append("\n");
            }
        }

        String content = contentBuilder.toString();
        if (content.isEmpty()) {
            return Collections.emptyList();
        }

        // 创建文档并设置元数据
        Map<String, Object> metadata = new HashMap<>();
        metadata.put("filename", file.getName());
        metadata.put("path", file.getPath());

        Document document = new Document(content, metadata);
        return Collections.singletonList(document);
    }

    /**
     * 删除目录
     */
    private void deleteDirectory(File directory) {
        if (directory == null || !directory.exists()) {
            return;
        }

        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    deleteDirectory(file);
                } else {
                    // 使用更健壮的文件删除方式
                    boolean deleted = file.delete();
                    if (!deleted && file.exists()) {
                        log.warn("无法删除文件: {}", file.getPath());
                        // 尝试强制删除
                        file.setWritable(true);
                        file.delete();
                    }
                }
            }
        }
        boolean deleted = directory.delete();
        if (!deleted && directory.exists()) {
            log.warn("无法删除目录: {}", directory.getPath());
        }
    }
}
